/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_orm.htm
 *
 * Zhiqim Orm is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.orm.datasource;

import java.lang.reflect.Method;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.zhiqim.kernel.extend.LinkedMapSO;
import org.zhiqim.kernel.extend.TreeMapSO;
import org.zhiqim.kernel.util.Classes;
import org.zhiqim.orm.ORMConstants;
import org.zhiqim.orm.dbo.Dbo;
import org.zhiqim.orm.dbo.DboField;


/**
 * 数据库结果集工具类
 *
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class ZResultSet implements ORMConstants
{
    /*******************************************************************************************/
    //解释结果集
    /*******************************************************************************************/
    
    /**
     * 解释结果集到哈唏表中
     * 
     * @param rst           结果集
     * @param resultClass   结果类
     * @return              结果表
     * @throws SQLException SQL异常
     */
    public static List<?> parseToBase(ResultSet rst, Class<?> resultClass) throws SQLException
    {
        if (!rst.next())
        {//先判断是否有数据
            return new ArrayList<>();
        }
        
        ArrayList<Object> resultList = new ArrayList<>();
        ResultSetMetaData rstMeta = rst.getMetaData();
        int columnCount = rstMeta.getColumnCount();

        do
        {
            if (columnCount == 1)
            {//只有一列时，取该值
                resultList.add(getValue(resultClass, rst, 1));
            }
            else
            {//多列时，取数据
                Object[] result = new Object[columnCount];
                for (int i=1;i<=columnCount;i++)
                {// 从1开始
                    result[i-1] = getValue(resultClass, rst, i);
                }

                resultList.add(result);
            }
        }
        while (rst.next());

        resultList.trimToSize();
        return resultList;
    }
    
    /**
     * 解释结果集到哈唏表中
     * 
     * @param rst           结果集
     * @param resultClass   结果类
     * @return              结果表
     * @throws SQLException SQL异常
     */
    public static List<?> parseToMap(ResultSet rst, Class<?> resultClass) throws SQLException
    {
        if (!rst.next())
        {//先判断是否有数据
            return new ArrayList<>();
        }
        
        ArrayList<Object> resultList = new ArrayList<>();
        ResultSetMetaData rstMeta = rst.getMetaData();
        int columnCount = rstMeta.getColumnCount();

        do
        {
            LinkedMapSO result = new LinkedMapSO(columnCount);

            for (int i = 1; i <= columnCount; i++)
            {// 从1开始
                int type = rstMeta.getColumnType(i);
                String columnName = rstMeta.getColumnName(i);
                switch (type)
                {
                case Types.CLOB:
                    Clob clob = rst.getClob(i);
                    if (clob != null)
                        result.put(columnName, clob.getSubString((long) 1, (int) clob.length()));
                    else
                        result.put(columnName, null);
                    break;

                default:
                    Object obj = rst.getObject(i);
                    result.put(columnName, obj);
                    break;
                }
            }

            if (resultClass == Map.class || resultClass == HashMap.class || resultClass == LinkedHashMap.class)
                resultList.add(result.instance());
            else if (resultClass == TreeMap.class)
                resultList.add(new TreeMap<String, Object>(result.instance()));
            else if (resultClass == TreeMapSO.class)
                resultList.add(new TreeMapSO(result));
            else
                resultList.add(result);
        }
        while (rst.next());

        resultList.trimToSize();
        return resultList;
    }
    
    /**
     * 解释结果集到DBO中
     * 
     * @param rst           结果集
     * @param resultDbo     结果DBO
     * @return              结果表
     * @throws SQLException SQL异常
     */
    public static List<?> parseToDbo(ResultSet rst, Dbo resultDbo) throws Exception
    {
        if (!rst.next())
        {//先判断是否有数据
            return new ArrayList<>();
        }
        
        ArrayList<Object> resultList = new ArrayList<>();
        ResultSetMetaData rstMeta = rst.getMetaData();
        int columnCount = rstMeta.getColumnCount();

        do
        {
            // 生成DBO
            Object result = Classes.newInstance(resultDbo.getName());
            for (int i = 1; i <= columnCount; i++)
            {// 从1开始
                String columnName = rstMeta.getColumnName(i);
                int columnType = rstMeta.getColumnType(i);
                parseToDboField(rst, result, resultDbo, columnName, columnType, i);
            }

            resultList.add(result);
        }
        while (rst.next());

        resultList.trimToSize();
        return resultList;
    }
    
    /**
     * 解释结果集到DBO中，使用反射调用结果集方法设置数据
     * 
     * @param rst           结果集
     * @param result        结果对象
     * @param dbo           数据库对象
     * @param columnName    列名
     * @param i             索引
     * @throws Exception    可能的异常
     */
    private static void parseToDboField(ResultSet rst, Object result, Dbo dbo, String columnName, int columnType, int i) throws Exception
    {
        //查找属性，先找列对应，后找属性对应
        DboField field = dbo.getField(columnName);
        if (field == null)
            return;
        
        Class<?> clazz = result.getClass();
        String method = field.getSetMethod();
        int type = field.getType();
        Object obj = null;
        Method m = null;
        
        switch (type)
        {
        //基本类型
        case Z_ORM_BOOLEAN_INT:     obj = rst.getBoolean(i);m = clazz.getMethod(method, boolean.class);break;
        case Z_ORM_BYTE_INT:        obj = rst.getInt(i);m = clazz.getMethod(method, int.class);break;
        case Z_ORM_SHORT_INT:       obj = rst.getInt(i);m = clazz.getMethod(method, int.class);break;
        case Z_ORM_INT_INT:         obj = rst.getInt(i);m = clazz.getMethod(method, int.class);break;
        case Z_ORM_LONG_INT:        obj = rst.getLong(i);m = clazz.getMethod(method, long.class);break;
        //小数和时间
        case Z_ORM_DECIMAL_INT:     obj = rst.getDouble(i);m = clazz.getMethod(method, double.class);break;
        case Z_ORM_DATETIME_INT:    obj = rst.getTimestamp(i);break;
        //字符串
        case Z_ORM_STRING_INT:
        {
            if (Types.CLOB == columnType)
            {//CLOB
                Clob clob = rst.getClob(i);
                if (clob != null)
                    obj = clob.getSubString(0, (int)clob.length());
            }
            else
            {//其他类型直接读取字符串
                obj = rst.getString(i);
            }
            break;
        }
        //二进制字节数组
        case Z_ORM_BINARY_INT:
        {
            if (Types.BLOB == columnType)
            {
                Blob blob = rst.getBlob(i);
                if (blob != null)
                    obj = blob.getBytes(1, (int)blob.length());
            }
            else
            {//其他类型直接读byte[]
                obj = rst.getBytes(i);
            }
            break;
        }
        default:
            obj = rst.getObject(i);
            break;
        }
        
        if (obj != null)
        {
            if (m == null)
                m = clazz.getMethod(method, obj.getClass());
            m.invoke(result, obj);
        }
    }
    
    /*******************************************************************************************/
    //从结果集或结果中读取对象
    /*******************************************************************************************/
    
    /**
     * 从结果集中读取对象
     * 
     * @param resultClass   结果类
     * @param rst           结果集
     * @param index         索引值
     * @return              索引位置的对象
     * @throws SQLException SQL异常
     */
    public static Object getValue(Class<?> resultClass, ResultSet rst, int index) throws SQLException
    {
        if (resultClass == boolean.class || resultClass == Boolean.class)
            return rst.getBoolean(index);
        else if (resultClass == byte.class || resultClass == Byte.class || resultClass == short.class || resultClass == Short.class || resultClass == int.class || resultClass == Integer.class)
            return rst.getInt(index);
        else if (resultClass == long.class || resultClass == Long.class)
            return rst.getLong(index);
        //日期类型
        else if (resultClass == Timestamp.class)
            return rst.getTimestamp(index);
        else if (resultClass == Date.class)
            return rst.getDate(index);
        else if (resultClass == Time.class)
            return rst.getTime(index);
        //字符串
        else if (resultClass == String.class)
            return rst.getString(index);
        //二进制
        else if (resultClass == byte[].class)
            return rst.getBytes(index);
        else
        {//其他类型
            return rst.getObject(index);
        }
    }
}
